// -----------------------------------------------------------------------
//  <copyright file="ResourceSynchronizer.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2025 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025/1/6 23:06</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Authorization.Domain;
using DaprPlus.Authorization.Domain.Entities;
using DaprPlus.Authorization.Domain.Specs;
using DaprPlus.Authorization.Properties;

using OSharp.Caching;
using OSharp.Collections;


namespace DaprPlus.Authorization.Application;

/// <summary>
/// 资源信息同步实现
/// </summary>
/// <param name="provider"></param>
public class ResourceSynchronizer(IServiceProvider provider) : IResourceSynchronizer
{
    private readonly ILogger _logger = provider.GetLogger<ResourceSynchronizer>();

    /// <summary>
    /// 同步资源信息
    /// </summary>
    /// <param name="serviceName">服务名称</param>
    /// <param name="resourceType">资源类型</param>
    /// <param name="resources">资源信息</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task SyncAsync(string serviceName, ResourceType resourceType, IResource[] resources, CancellationToken cancellationToken = default)
    {
        var uow = provider.GetUnitOfWork(true);
        var resourceService = provider.GetRequiredService<IResourceService>();
        if (resources.Length == 0)
        {
            // 提取的资源数为0，删除指定服务中的所有相应类型的资源
            await resourceService.DeleteResourceAsync(serviceName, resourceType, cancellationToken);
            await uow.CommitAsync(cancellationToken);
            return;
        }

        //验证签名，确定是否进行同步操作
        var repository = provider.GetRequiredService<IQueryRepository<Resource>>();
        var specification = new ResourceByServiceNameAndTypeSpec(serviceName, resourceType);
        var hashEntities = resources.Cast<IEntityHash>().ToArray();
        var entityType = hashEntities[0].GetType().FullName;
        var hash = hashEntities.Select(m => m.GetHash()).ExpandAndToString().ToMd5Hash();
        var checkHashKey = $"Initialize:EntityHash:{serviceName}:{resourceType}";
        var cache = provider.GetDistributedCache();
        var lastHash = await cache.GetAsync<string>(checkHashKey, cancellationToken);
        if (lastHash == hash)
        {
            _logger.LogInformation(ResAuthorization.Format_Init_Sync_EntityData_Same, hashEntities.Length, entityType, hash);
            var existCount = await repository.GetCountAsync(specification, cancellationToken);
            if (existCount > 0)
            {
                _logger.LogInformation(ResAuthorization.Format_Auth_Resource_SyncCancel, serviceName, resourceType);
                return;
            }
            return;
        }

        _logger.LogInformation(ResAuthorization.Format_Init_Sync_EntityData_NotSame, hashEntities.Length, entityType, hash, lastHash);

        var dbResources = await repository.GetAllAsync(specification, cancellationToken);

        //删除的资源
        var removeResources = dbResources.Except(resources,
            EqualityHelper<IResource>.CreateComparer(m => m.Code + m.ServiceName + m.Type)).ToArray();
        var removeCount = removeResources.Length;
        if (removeCount > 0)
        {
            foreach (var id in removeResources.Select(m => m.Id))
            {
                await resourceService.DeleteResourceAsync(id, cancellationToken);
            }
        }

        //新增的资源
        var addResources = resources.Except(dbResources,
            EqualityHelper<IResource>.CreateComparer(m => m.Code + m.ServiceName + m.Type)).ToArray();
        var addCount = addResources.Length;
        if (addCount > 0)
        {
            foreach (var resource in addResources)
            {
                await resourceService.CreateResourceAsync(resource, cancellationToken);
            }
        }

        //更新的资源
        var updateCount = 0;
        var updateResources = resources.Except(addResources).ToArray();
        foreach (var resource in updateResources)
        {
            var result = await resourceService.UpdateResourceAsync(resource, true, cancellationToken);
            if (result.IsSuccess())
            {
                updateCount++;
            }
        }
        await uow.CommitAsync(cancellationToken);
        await cache.SetAsync(checkHashKey, hash, token: cancellationToken);

        _logger.LogInformation(ResAuthorization.Format_Auth_Resource_SyncEnd, resourceType, serviceName, addCount, updateCount, removeCount);
    }
}
